<?php
/*
 * @Author: CHINAKAIYUAN<sunkaiyuan@namenode.cn>
 * @Date: 2022-02-03 01:21:39
 * @LastEditTime: 2023-11-09 13:20:49
 * @LastEditors: 孙开源 && sunkaiyuan@namenode.cn
 * @Description: 
 */

namespace App\Traits;

use App\Models\AutoCode;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use ReflectionObject;

trait AutoCodeTrait
    {
    /**
     * 自动编码模型
     *
     */
    protected static $_autocode;

    /**
     * 最大值id缓存
     *
     * @var array
     */
    protected $maximums = [];

    /**
     * 标识
     *
     * @var string
     */

    public static $_staticAcSign = null;
    /**
     * 编号所用的字段
     *
     * @var string
     */
    protected $_ACCode = 'code';


    /**
     * 编码的前缀(特殊编码格式)
     *
     * @var string
     */
    protected static $_ACPrefix = '';


    protected function getAutoCodeRepository()
        {
        $model = self::$_autocode == null ? self::$_autocode = App::make(AutoCode::class) : self::$_autocode;
        return $model;
        }

    protected function setCode($code = '')
        {
        if (isNotEmpty($this->ACCode)) {
            $this->_ACCode = $this->ACCode;
            }
        if ($code != '')
            $this->_ACCode = $code;
        return $this;
        }

    public function getACCode()
        {
        return isNotEmpty($this->ACCode) ? $this->ACCode : $this->_ACCode;
        }
    public function setACSignAttribute($ACSign)
        {

        $this::$_staticAcSign = $ACSign;

        return $this;
        }
    public function setACSign($ACSign)
        {
        if (isEmpty($this::$_staticAcSign)) {
            $this::$_staticAcSign = $this->ACSign;
            }

        if ($ACSign != '')
            $this::$_staticAcSign = $ACSign;

        return $this;
        }
    public function getACSign()
        {
        return $this::$_staticAcSign;
        }
    public function setPrefix($prefix)
        {
        if ($prefix != '')
            $this::$_ACPrefix = $prefix;
        return $this;
        }

    protected function verifyACSign()
        {

        if (isEmpty($this::$_staticAcSign)) {
            fail('编码格式标识未设置,请在MODEL中定义初始化ACSign变量，或者调用generateCode 函数时，传入ACSign参数');
            }
        return $this;
        }
    public function scopeGenerateCode($query, $ACSign = '', $code = '', $prefix = '')
        {
        return $this->generateCode($ACSign = '', $code = '', $prefix = '');
        }
    public function generateCode($ACSign = '', $code = '', $prefix = '')
        {
        $this->setACSign($ACSign);
        $this->setCode($code);
        $this->setPrefix($prefix);
        $this->verifyACSign();

        if (isNotEmpty($this->{$this->_ACCode})) {
            return $this;
            }
        if (!empty($this->id)) {
            $this->where("id", "<>", $this->id);
            }
        $this->{$this->_ACCode} = $this->gcode();
        return $this;
        }

    protected function increase($rule)
        {
        // 获取x的次数,确定序列号的位数
        if (preg_match('/\{{1}x+\}{1}/', $rule, $matches)) {
            $replace    = $matches[0];
            $width      = strlen($replace) - 2;
            $codePrefix = str_replace($replace, '', $rule);
            // 匹配固定长度的字符
            $modeString = str_pad('', $width, '_');

            // 默认递增
            if (empty($this->maximums) || empty($this->maximums[$this::$_staticAcSign])) {

                $query = $this->select(DB::raw("ifnull(max(substring({$this->_ACCode}, -{$width})),0) as maxid"));

                $query->where($this->_ACCode, 'like', $codePrefix . $modeString);
                $query->where(DB::raw("(substring({$this->_ACCode}, -{$width}) REGEXP '[^0-9]')"), 0);

                if (!empty($this->id)) {
                    $query->where('id', "<>", $this->id);
                    }
                $reflection = new ReflectionObject($this);
                if (in_array('App\Traits\SoftDeletesTrait', $reflection->getTraitNames()))
                    $query->withTrashed();
                // DB::enableQueryLog();
                $last = $query->lockForUpdate()->first();
                // dd(DB::getQueryLog());  
                if ($last)
                    $maxid = (int) $last->maxid;
                else {
                    $maxid = 0;
                    }
                $maxid = $this->maximums[$this::$_staticAcSign] = ++$maxid;
                }
            else {
                $maxid = ++$this->maximums[$this::$_staticAcSign];
                }

            $maxid = $maxid . '';
            $rule  = str_replace($replace, (str_pad($maxid, $width, '0', STR_PAD_LEFT)), $rule);
            }

        return $rule;
        }

    protected function random($rule)
        {
        // 获取r的次数,确定序列号的位数
        if (preg_match('/\{{1}r+\}{1}/', $rule, $matches)) {
            $replace = $matches[0];
            $width   = strlen($replace) - 2;
            $random  = '';

            $i = 0;
            while ($i < $width) {
                $random .= rand(0, 9);
                $i++;
                }

            $rule = str_replace($replace, $random, $rule);
            }

        return $rule;
        }

    /**
     * 根据规则生成自动编号
     * @param  string $key
     * @param  string $prefix
     * @return string
     */
    protected function gcode()
        {
        // DB::enableQueryLog();
        $data = $this->getAutoCodeRepository()->where(['key' => $this::$_staticAcSign])->first();
        // 
        // dd(DB::getQueryLog());
        if (empty($data) || empty($data['setting'])) {
            fail("自动编码[{$this::$_staticAcSign}]未定义，参照 [系统设置] - [自动编码]。");
            }

        // 自动编号生成规则
        $rule = $data['setting'];

        $formatter = 'Y-m-d-H-i-s';
        $fmts      = explode('-', $formatter);
        $dateFmts  = explode('-', date($formatter));

        foreach (array_flip($fmts) as $k => $v) {
            $data[$k] = $dateFmts[$v];
            }
        // 前缀
        $data['p'] = $this::$_ACPrefix;

        // 获取2位年份
        $data['y'] = substr($data['Y'], 2);

        // str_replace 比正则表达式 快很多
        $rule = str_replace('{Y}', $data['Y'], $rule);
        $rule = str_replace('{y}', $data['y'], $rule);
        $rule = str_replace('{m}', $data['m'], $rule);
        $rule = str_replace('{d}', $data['d'], $rule);
        $rule = str_replace('{H}', $data['H'], $rule);
        $rule = str_replace('{i}', $data['i'], $rule);
        $rule = str_replace('{s}', $data['s'], $rule);
        $rule = str_replace('{p}', $data['p'], $rule);

        // 获取用户标识

        $rule = str_replace('{u}', auth()->user()?->name, $rule);

        $rule = $this->increase($rule);

        $rule = $this->random($rule);

        return $rule;
        }
    }